using MailKit.Net.Smtp;
using MailKit.Security;

using MimeKit;
using MimeKit.Text;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tool
{
    public class EmailHelper
    {
        public static void SetConfig(string smtpHost, int smtpPort, bool isSsl, string loginAccount, string loginPassword)
        {
            SmtpHost = smtpHost;
            SmtpPort = smtpPort;
            IsSsl = isSsl;
            LoginAccount = loginAccount;
            LoginPassword = loginPassword;
        }

        public static void SetConfig(EmailSendConfig sendConfig)
        {
            SmtpHost = sendConfig.SmtpHost;
            SmtpPort = sendConfig.SmtpPort;
            IsSsl = sendConfig.IsSsl;
            LoginAccount = sendConfig.LoginAccount;
            LoginPassword = sendConfig.LoginPassword;
        }

        /// <summary>
        /// 邮箱SMTP服务器地址
        /// </summary>
        private static string SmtpHost { get; set; }

        /// <summary>
        /// 邮箱SMTP服务器端口
        /// </summary>
        private static int SmtpPort { get; set; }

        /// <summary>
        /// 是否启用IsSsl
        /// </summary>
        private static bool IsSsl { get; set; }

        /// <summary>
        /// 发件人账号
        /// </summary>
        private static string LoginAccount { get; set; }

        /// <summary>
        /// 发件人密码
        /// </summary>
        private static string LoginPassword { get; set; }

        //参考地址：https://blog.csdn.net/starfd/article/details/80706227
        ///// <summary>
        ///// 发送电子邮件，默认发送方为<see cref="UserAddress"/>
        ///// </summary>
        ///// <param name="subject">邮件主题</param>
        ///// <param name="content">邮件内容主题</param>
        ///// <param name="toAddress">接收方信息</param>
        ///// <param name="textFormat">内容主题模式，默认TextFormat.Text</param>
        ///// <param name="attachments">附件</param>
        ///// <param name="dispose">是否自动释放附件所用Stream</param>
        ///// <returns></returns>
        //public static async Task SendEMailAsync(string subject, string content, IEnumerable<MailboxAddress> toAddress, TextFormat textFormat = TextFormat.Text, IEnumerable<AttachmentInfo> attachments = null, bool dispose = true)
        //{
        //    await SendEMailAsync(subject, content, new MailboxAddress[] { new MailboxAddress(UserName, UserAddress) }, toAddress, textFormat, attachments, dispose).ConfigureAwait(false);
        //}

        ///// <summary>
        ///// 发送电子邮件
        ///// </summary>
        ///// <param name="subject">邮件主题</param>
        ///// <param name="content">邮件内容主题</param>
        ///// <param name="fromAddress">发送方信息</param>
        ///// <param name="toAddress">接收方信息</param>
        ///// <param name="textFormat">内容主题模式，默认TextFormat.Text</param>
        ///// <param name="attachments">附件</param>
        ///// <param name="dispose">是否自动释放附件所用Stream</param>
        ///// <returns></returns>
        //public static async Task SendEMailAsync(string subject, string content, MailboxAddress fromAddress, IEnumerable<MailboxAddress> toAddress, TextFormat textFormat = TextFormat.Text, IEnumerable<AttachmentInfo> attachments = null, bool dispose = true)
        //{
        //    await SendEMailAsync(subject, content, new MailboxAddress[] { fromAddress }, toAddress, textFormat, attachments, dispose).ConfigureAwait(false);
        //}

        ///// <summary>
        ///// 发送电子邮件
        ///// </summary>
        ///// <param name="subject">邮件主题</param>
        ///// <param name="content">邮件内容主题</param>
        ///// <param name="fromAddress">发送方信息</param>
        ///// <param name="toAddress">接收方信息</param>
        ///// <param name="textFormat">内容主题模式，默认TextFormat.Text</param>
        ///// <param name="attachments">附件</param>
        ///// <param name="dispose">是否自动释放附件所用Stream</param>
        ///// <returns></returns>
        //public static async Task SendEMailAsync(string subject, string content, IEnumerable<MailboxAddress> fromAddress, IEnumerable<MailboxAddress> toAddress, TextFormat textFormat = TextFormat.Text, IEnumerable<AttachmentInfo> attachments = null, bool dispose = true)
        //{
        //    var message = new MimeMessage();
        //    message.From.AddRange(fromAddress);
        //    message.To.AddRange(toAddress);
        //    message.Subject = subject;
        //    var body = new TextPart(textFormat)
        //    {
        //        Text = content
        //    };
        //    MimeEntity entity = body;
        //    if (attachments != null)
        //    {
        //        var mult = new Multipart("mixed")
        //        {
        //            body
        //        };
        //        foreach (var att in attachments)
        //        {
        //            if (att.Stream != null)
        //            {
        //                var attachment = string.IsNullOrWhiteSpace(att.ContentType) ? new MimePart() : new MimePart(att.ContentType);
        //                attachment.Content = new MimeContent(att.Stream);
        //                attachment.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment);
        //                attachment.ContentTransferEncoding = att.ContentTransferEncoding;
        //                attachment.FileName = ConvertHeaderToBase64(att.FileName, Encoding.UTF8);//解决附件中文名问题
        //                mult.Add(attachment);
        //            }
        //        }
        //        entity = mult;
        //    }
        //    message.Body = entity;
        //    message.Date = DateTime.Now;

        //    var sendResultData = new ResultData<string>();

        //    using (var client = new SmtpClient())
        //    {
        //        //创建连接

        //        //await Connection(client, sendResultData);
        //        //await Authenticate(client, sendResultData);
        //        //await Send(client, null, sendResultData);
        //        await client.ConnectAsync(SmtpHost, SmtpPort, IsSsl).ConfigureAwait(false);
        //        await client.AuthenticateAsync(SenderAccount, SenderPassword).ConfigureAwait(false);
        //        await client.SendAsync(message).ConfigureAwait(false);
        //        await client.DisconnectAsync(true).ConfigureAwait(false);
        //        if (dispose && attachments != null)
        //        {
        //            foreach (var att in attachments)
        //            {
        //                att.Dispose();
        //            }
        //        }
        //    }
        //}

        public static async Task<ResultData<string>> SendAsync(EmailSendContent sendContent)
        {
            var sendResultData = new ResultData<string>();

            #region 验证邮件发送配置信息

            if (string.IsNullOrWhiteSpace(SmtpHost))
            {
                sendResultData.Errors = "主机名 不能为空";
                return sendResultData;
            }
            if (SmtpPort <= 0)
            {
                sendResultData.Errors = $"Smtp端口设置错误:{SmtpPort}";
                return sendResultData;
            }
            if (string.IsNullOrWhiteSpace(LoginPassword))
            {
                sendResultData.Errors = "发送邮件密码 不能为空";
                return sendResultData;
            }
            if (string.IsNullOrWhiteSpace(LoginAccount))
            {
                sendResultData.Errors = "发送邮件账户 不能为空";
                return sendResultData;
            }

            #endregion 验证邮件发送配置信息

            try
            {
                using var client = new SmtpClient();
                await Connection(client, sendResultData);
                if (sendResultData.Succeeded == false)
                {
                    return sendResultData;
                }
                await Authenticate(client, sendResultData);
                if (sendResultData.Succeeded == false)
                {
                    return sendResultData;
                }
                await Send(client, sendContent, sendResultData);
                if (sendResultData.Succeeded == false)
                {
                    return sendResultData;
                }
                sendResultData.Succeeded = true;
                sendResultData.Errors = "发送成功";
            }
            catch (Exception e)
            {
                sendResultData.Errors = $"发送邮失败\r\n{e.ToStringEx()}";
            }

            return sendResultData;
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="client">客户端对象</param>
        /// <param name="sendResultData">发送结果</param>
        private static async Task Connection(SmtpClient client, ResultData<string> sendResultData)
        {
            try
            {
                await client.ConnectAsync(SmtpHost, SmtpPort, IsSsl);
                sendResultData.Succeeded = true;
            }
            catch (SmtpCommandException ex)
            {
                sendResultData.Errors = $"尝试连接时出错:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
            catch (SmtpProtocolException ex)
            {
                sendResultData.Errors = $"尝试连接时的协议错误:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
            catch (Exception ex)
            {
                sendResultData.Errors = $"服务器连接错误:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
        }

        /// <summary>
        /// 账户认证
        /// </summary>
        /// <param name="client">客户端对象</param>
        /// <param name="sendResultData">发送结果</param>
        private static async Task Authenticate(SmtpClient client, ResultData<string> sendResultData)
        {
            try
            {
                await client.AuthenticateAsync(LoginAccount, LoginPassword);
                sendResultData.Succeeded = true;
            }
            catch (AuthenticationException ex)
            {
                sendResultData.Errors = $"无效的用户名或密码:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
            catch (SmtpCommandException ex)
            {
                sendResultData.Errors = $"尝试验证错误:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
            catch (SmtpProtocolException ex)
            {
                sendResultData.Errors = $"尝试验证时的协议错误:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
            catch (Exception ex)
            {
                sendResultData.Errors = $"账户认证错误:{0}" + ex.Message;
                sendResultData.Succeeded = false;
            }
        }

        /// <summary>
        /// 发送邮件
        /// </summary>
        /// <param name="emailSendContent">邮件内容</param>
        /// <param name="client">客户端对象</param>
        /// <param name="sendResultData">发送结果</param>
        private static async Task Send(SmtpClient client, EmailSendContent emailSendContent,
         ResultData<string> sendResultData)
        {
            try
            {
                //  client.Send(MailMessage.AssemblyMailMessage(mailBodyEntity));
                var message = new MimeMessage();
                message.Date = DateTimeOffset.Now;
                //发件人信息
                message.From.Add(emailSendContent.SendInfo);
                //收件人信息
                foreach (var mailboxAddress in emailSendContent.ToAddress)
                {
                    message.To.Add(mailboxAddress);
                }

                //主题
                message.Subject = emailSendContent.Subject;
                //内容
                var body = new TextPart(TextFormat.Html)
                {
                    Text = emailSendContent.Body
                };

                MimeEntity entity = body;
                if (emailSendContent.Attachments != null)
                {
                    var mult = new Multipart("mixed")
                    {
                        body
                    };
                    foreach (var att in emailSendContent.Attachments)
                    {
                        if (att.Stream != null)
                        {
                            var attachment = string.IsNullOrWhiteSpace(att.ContentType) ? new MimePart() : new MimePart(att.ContentType);
                            attachment.Content = new MimeContent(att.Stream);
                            attachment.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment);
                            attachment.ContentTransferEncoding = att.ContentTransferEncoding;
                            attachment.FileName = ConvertHeaderToBase64(att.FileName, Encoding.UTF8);//解决附件中文名问题
                            mult.Add(attachment);
                        }
                    }
                    entity = mult;
                }
                message.Body = entity;

                await client.SendAsync(message);//发送邮件
                sendResultData.Succeeded = true;
            }
            catch (SmtpCommandException ex)
            {
                switch (ex.ErrorCode)
                {
                    case SmtpErrorCode.RecipientNotAccepted:
                        sendResultData.Errors = $"收件人未被接受:{ex.Message}";
                        break;

                    case SmtpErrorCode.SenderNotAccepted:
                        sendResultData.Errors = $"发件人未被接受:{ex.Message}";
                        break;

                    case SmtpErrorCode.MessageNotAccepted:
                        sendResultData.Errors = $"消息未被接受:{ex.Message}";
                        break;
                }
                sendResultData.Succeeded = false;
            }
            catch (SmtpProtocolException ex)
            {
                sendResultData.Errors = $"发送消息时的协议错误:{ex.Message}";
                sendResultData.Succeeded = false;
            }
            catch (Exception ex)
            {
                sendResultData.Errors = $"邮件接收失败:{ex.Message}";
                sendResultData.Succeeded = false;
            }
        }

        private static string ConvertToBase64(string inputStr, Encoding encoding)
        {
            return Convert.ToBase64String(encoding.GetBytes(inputStr));
        }

        private static string ConvertHeaderToBase64(string inputStr, Encoding encoding)
        {
            //https://www.cnblogs.com/qingspace/p/3732677.html
            var encode = !string.IsNullOrEmpty(inputStr) && inputStr.Any(c => c > 127);
            if (encode)
            {
                return "=?" + encoding.WebName + "?B?" + ConvertToBase64(inputStr, encoding) + "?=";
            }
            return inputStr;
        }
    }

    /// <summary>
    /// 附件信息
    /// </summary>
    public class AttachmentInfo : IDisposable
    {
        /// <summary>
        /// 附件类型，比如application/pdf
        /// </summary>
        public string ContentType { get; set; }

        /// <summary>
        /// 文件名称
        /// </summary>
        public string FileName { get; set; }

        /// <summary>
        /// 文件传输编码方式，默认ContentEncoding.Default
        /// </summary>
        public ContentEncoding ContentTransferEncoding { get; set; } = ContentEncoding.Default;

        /// <summary>
        /// 文件数组
        /// </summary>
        public byte[] Data { get; set; }

        private Stream stream;

        /// <summary>
        /// 文件数据流，获取数据时优先采用此部分
        /// </summary>
        public Stream Stream {
            get {
                if (this.stream == null && this.Data != null)
                {
                    stream = new MemoryStream(this.Data);
                }
                return this.stream;
            }
            set { this.stream = value; }
        }

        /// <summary>
        /// 释放Stream
        /// </summary>
        public void Dispose()
        {
            if (this.stream != null)
            {
                this.stream.Dispose();
            }
        }
    }

    /// <summary>
    /// 邮件发送服务器配置
    /// </summary>
    public class EmailSendConfig
    {
        /// <summary>
        /// 邮箱SMTP服务器地址
        /// </summary>
        public string SmtpHost { get; set; }

        /// <summary>
        /// 邮箱SMTP服务器端口
        /// </summary>
        public int SmtpPort { get; set; }

        /// <summary>
        /// 是否启用IsSsl
        /// </summary>
        public bool IsSsl { get; set; }

        /// <summary>
        /// 发件人账号
        /// </summary>
        public string LoginAccount { get; set; }

        /// <summary>
        /// 发件人密码
        /// </summary>
        public string LoginPassword { get; set; }

        /// <summary>
        /// 发送账号名
        /// </summary>
        public string SenderUserName { get; set; }

        /// <summary>
        /// 发送账号邮箱
        /// </summary>
        public string SenderUserEmail { get; set; }
    }

    /// <summary>
    /// 邮件发送内容
    /// </summary>
    public class EmailSendContent
    {
        /// <summary>
        /// 主题
        /// </summary>
        public string Subject { get; set; }

        /// <summary>
        /// 内容
        /// </summary>
        public string Body { get; set; }

        /// <summary>
        /// 发送邮件信息
        /// </summary>
        public MailboxAddress SendInfo { get; set; }

        /// <summary>
        /// 接收邮件地址列表
        /// </summary>
        public List<MailboxAddress> ToAddress { get; set; } = new List<MailboxAddress>();

        /// <summary>
        /// 邮件内容格式
        /// </summary>
        public TextFormat BodyFormat { get; set; } = TextFormat.Html;

        /// <summary>
        /// 附件
        /// </summary>
        public IEnumerable<AttachmentInfo> Attachments { get; set; }

        /// <summary>
        /// 是否自动释放附件所用Stream
        /// </summary>
        public bool IsAutoDisposeAttachmentStream { get; set; } = true;

        ///// <summary>
        ///// 接收用户
        ///// </summary>
        //public string ReceiveUser { get; set; }
        ///// <summary>
        ///// 接收邮件邮箱
        ///// </summary>
        //public string ReceiveEmail { get; set; }

        ///// <summary>
        ///// 发送来源名称
        ///// </summary>
        //public string SenderDisplayName { get; set; }
        ///// <summary>
        ///// 发送来源
        ///// </summary>
        //public string SenderEmail { get; set; }
    }
}